<?php
 /**
  * TweetPHP
  *
  * @author Jonathan Nicol @f6design
  * @version 1.0.6
  * @license The MIT License http://opensource.org/licenses/mit-license.php
  * @link  http://f6design.com/journal/2013/06/20/tweetphp-display-tweets-on-your-website-using-php/
  * 
  * Notes:
  * To interact with Twitter's API you will need to create an API KEY:
  * https://dev.twitter.com/apps
  * After creating your API Key you will need to pass the following values to the class
  * constructor: "Consumer key", "Consumer secret", "Access token", "Access token secret"
  * --
  * Options can be overridden by passing an array of key/value pairs to the class
  * constructor. At a minimum you must set the consumer_key, consumer_secret, access_token,
  * access_token_secret and twitter_screen_name options.
  * --
  * You may also need to change the cache_file option to point at a directory/file on your
  * web server. Caching is employed because Twitter rate limits how many times their feeds
  * can be accessed per hour.
  * 
  * Credits:
  * Feed parsing: https://github.com/themattharris/tmhOAuth
  * Hashtag/username parsing: https://github.com/mikenz/twitter-text-php
  */
 class TweetPHP {
    private $tmhOAuth;
    private $options;
    private $tweet_found = false;
    private $tweet_count = 0;
    private $tweet_list;
    private $tweet_array;
    private $debug_report = array();

    /**
     * Initialize a new TweetPHP object
     */
    public function  __construct ($options = array()) {

      $this->options = array_merge(
      array(
          'consumer_key'          => '',
          'consumer_secret'       => '',
          'access_token'          => '',
          'access_token_secret'   => '',
          'twitter_screen_name'   => '',
          'cache_file'            => dirname(__FILE__) . '/cache/twitter.txt', // Where on the server to save the cached formatted tweets
          'cache_file_raw'        => dirname(__FILE__) . '/cache/twitter-array.txt', // Where on the server to save the cached raw tweets
          'cachetime'             => 60 * 60, // Seconds to cache feed (1 hour).
          'tweets_to_display'     => 10, // How many tweets to fetch
          'ignore_replies'        => true, // Ignore @replies
          'ignore_retweets'       => true, // Ignore retweets
          'twitter_style_dates'   => false, // Use twitter style dates e.g. 2 hours ago
          'twitter_date_text'     => array('seconds', 'minutes', 'about', 'hour', 'ago'),
          'date_format'           => '%I:%M %p %b %e%O', // The defult date format e.g. 12:08 PM Jun 12th. See: http://php.net/manual/en/function.strftime.php
          'date_lang'             => null, // Language for date e.g. 'fr_FR'. See: http://php.net/manual/en/function.setlocale.php
          'format'                => 'html', // Can be 'html' or 'array'
          'twitter_wrap_open'     => '<h2>Latest tweets</h2><ul id="twitter">',
          'twitter_wrap_close'    => '</ul>',
          'tweet_wrap_open'       => '<li><span class="status">',
          'meta_wrap_open'        => '</span><span class="meta"> ',
          'meta_wrap_close'       => '</span>',
          'tweet_wrap_close'      => '</li>',
          'error_message'         => 'Oops, our twitter feed is unavailable right now.',
          'error_link_text'       => 'Follow us on Twitter',
          'debug'                 => false
        ),
        $options
      );

      if ($this->options['debug']) {
        error_reporting(E_ALL);
      }

      if ($this->options['date_lang']) {
        setlocale(LC_ALL, $this->options['date_lang']);
      }

      $cache_file_timestamp = ((file_exists($this->options['cache_file']))) ? filemtime($this->options['cache_file']) : 0;
      $this->add_debug_item('Cache expiration timestamp: ' . (time() - $this->options['cachetime']));
      $this->add_debug_item('Cache file timestamp: ' . $cache_file_timestamp);

      // Show file from cache if still valid.
      if (time() - $this->options['cachetime'] < $cache_file_timestamp) {
        $this->tweet_found = true;
        $this->add_debug_item('Cache file is newer than cachetime.');
        $this->tweet_list = file_get_contents($this->options['cache_file']);  
        $this->tweet_array = unserialize(file_get_contents($this->options['cache_file_raw']));
      } else {
        $this->add_debug_item("Cache file doesn't exist or is older than cachetime.");
        $this->fetch_tweets();
      }

      // In case the feed did not parse or load correctly, show a link to the Twitter account.
      if (!$this->tweet_found) {
        $this->add_debug_item('No tweets were found. error_message will be displayed.');
        $this->tweet_list = $this->options['twitter_wrap_open'] . $this->options['tweet_wrap_open'] . $this->options['error_message'] . ' ' . $this->options['meta_wrap_open'] .'<a href="http://twitter.com/' . $this->options['twitter_screen_name'] . '">' . $this->options['error_link_text'] . '</a>' . $this->options['meta_wrap_close'] . $this->options['tweet_wrap_close'] . $this->options['twitter_wrap_close'];
        $this->tweet_array = array('Error fetching or loading tweets');
      }
    }

    /**
     * Fetch tweets using Twitter API
     */
    private function fetch_tweets () {
      $this->add_debug_item('Fetching fresh tweets using Twitter API.');

      require_once(dirname(__FILE__) . '/lib/tmhOAuth/tmhOAuth.php');
      
      // Creates a tmhOAuth object.
      $this->tmhOAuth = new tmhOAuth(array(
        'consumer_key'    => $this->options['consumer_key'],
        'consumer_secret' => $this->options['consumer_secret'],
        'token'           => $this->options['access_token'],
        'secret'          => $this->options['access_token_secret']
      ));

      // Request Twitter timeline.
      $params = array(
        'screen_name' => $this->options['twitter_screen_name']
      );
      if ($this->options['ignore_retweets']) {
        $params['include_rts'] = 'false';
      }
      if ($this->options['ignore_replies']) {
        $params['exclude_replies'] = 'true';
      }
      $response_code = $this->tmhOAuth->request('GET', $this->tmhOAuth->url('1.1/statuses/user_timeline.json'), $params);

      $this->add_debug_item('tmhOAuth response code: ' . $response_code);
      
      if ($response_code == 200) {
        $data = json_decode($this->tmhOAuth->response['response'], true);

        // Open the twitter wrapping element.
        $html = $this->options['twitter_wrap_open'];

        // Iterate over tweets.
        foreach($data as $tweet) {
          $html .=  $this->parse_tweet($tweet);
          // If we have processed enough tweets, stop.
          if ($this->tweet_count >= $this->options['tweets_to_display']){
            break;
          }
        }

        // Close the twitter wrapping element.
        $html .= $this->options['twitter_wrap_close'];

        // Save the formatted tweet list to a file. 
        $file = fopen($this->options['cache_file'], 'w');
        fwrite($file, $html); 
        fclose($file);

        // Save the raw data array to a file. 
        $file = fopen($this->options['cache_file_raw'], 'w');
        fwrite($file, serialize($data)); 
        fclose($file);

        $this->tweet_list = $html;
        $this->tweet_array = $data;
      } else {
        $this->add_debug_item('Bad tmhOAuth response code.');
      }
    }

    /**
     * Parse an individual tweet
     */
    private function parse_tweet ($tweet) {
      $this->tweet_found = true;
      $this->tweet_count++;

      // Format tweet text
      $tweet_text_raw = $tweet['text'];
      $tweet_text = $this->autolink($tweet_text_raw);

      // Tweet date is in GMT. Convert to UNIX timestamp in the local time of the tweeter.
      $utc_offset = $tweet['user']['utc_offset'];
      $tweet_time = strtotime($tweet['created_at']) + $utc_offset;

      if ($this->options['twitter_style_dates']){
        // Convert tweet timestamp into Twitter style date ("About 2 hours ago")
        $current_time = time();
        $time_diff = abs($current_time - $tweet_time);
        switch ($time_diff) {
          case ($time_diff < 60):
            $display_time = $time_diff . ' ' . $this->options['twitter_date_text'][0] . ' ' . $this->options['twitter_date_text'][4];
            break;      
          case ($time_diff >= 60 && $time_diff < 3600):
            $min = floor($time_diff/60);
            $display_time = $min . ' ' . $this->options['twitter_date_text'][1] . ' ' . $this->options['twitter_date_text'][4];
            break;      
          case ($time_diff >= 3600 && $time_diff < 86400):
            $hour = floor($time_diff/3600);
            $display_time = $this->options['twitter_date_text'][2] . ' ' . $hour . ' ' . $this->options['twitter_date_text'][3];
            if ($hour > 1){ $display_time .= 's'; }
            $display_time .= ' ' . $this->options['twitter_date_text'][4];
            break;          
          default: 
            $format = str_replace('%O', date('S', $tweet_time), $this->options['date_format']);
            $display_time = strftime($format, $tweet_time);
            break;
        }
      } else {
        $format = str_replace('%O', date('S', $tweet_time), $this->options['date_format']);
        $display_time = strftime($format, $tweet_time);
      }

      $href = 'http://twitter.com/' . $tweet['user']['screen_name'] . '/status/' . $tweet['id_str'];
      return $this->options['tweet_wrap_open'] . $tweet_text . $this->options['meta_wrap_open'] . '<a href="' . $href . '">' . $display_time . '</a>' . $this->options['meta_wrap_close'] . $this->options['tweet_wrap_close'];
    }

    /**
     * Add a debuggin item.
     */
    private function add_debug_item ($msg) {
      array_push($this->debug_report, $msg);
    }

    /**
     * Get debugging information as an HTML list.
     */
    public function get_debug_list () {
      $debug_list = '<ul>';
      foreach($this->debug_report as $debug_item) {
        $debug_list .= '<li>' . $debug_item . '</li>';
      }
      $debug_list .= '</ul>';
      return $debug_list;
    }

    /**
     * Get debugging information as an array.
     */
    public function get_debug_array () {
      return $this->debug_report;
    }

    /**
     * Helper function to convert usernames, hashtags and URLs
     * in a tweet to HTML links.
     */
    public function autolink ($tweet) {
      require_once(dirname(__FILE__) . '/lib/twitter-text-php/lib/Twitter/Autolink.php');

      $autolinked_tweet = Twitter_Autolink::create($tweet, false)
        ->setNoFollow(false)->setExternal(false)->setTarget('')
        ->setUsernameClass('')
        ->setHashtagClass('')
        ->setURLClass('')
        ->addLinks();

      return $autolinked_tweet;
    }

    /**
     * Get tweets as HTML list
     */
    public function get_tweet_list () {
      if ($this->options['debug']) {
        return $this->get_debug_list() . $this->tweet_list;
      } else {
        return $this->tweet_list;
      }
    }

    /**
     * Get tweets as an array
     */
    public function get_tweet_array () {
      return $this->tweet_array;
    }

}
?>
